﻿using System;
using System.Net;
using System.Net.Sockets;

namespace UFrame
{
    public class Connector
    {
        public event EventHandler<EventArgs> OnConnected;
        public event EventHandler<EventArgs> OnConnectFailed;
        public event EventHandler<EventArgs> OnDisconnected;
        public event EventHandler<NetEventArg_Received> OnReceived;

        private TcpClient mTcpClient = null; // tcp client
        private NetworkStream mNetStream = null; // network stream
        private IPAddress mServerIpAdd = null; // ip
        private int mServerPort = 0; // port

        private eNetState mCurNetState = eNetState.NS_None; // state of the net
        private BufferInfo mBufferInfo = new BufferInfo(); // info of the buffer
        private byte[] mByHead = new byte[NetDefine.HEAD_LEN_SIZE]; // buffer of the head
        private byte[] mByReceiveBuf = new byte[NetDefine.BUFFER_LENGTH]; // buffer of the content

        public Connector(string ip, int port)
        {
            mServerIpAdd = IPAddress.Parse(ip);
            mServerPort = port;
        }


        //属性
        public eNetState NetState
        {
            get { return mCurNetState; }
        }

        private void NetDrop(eNetError reason)
        {
            switch (reason)
            {
                case eNetError.NE_ReadBufferOverflow:
                    PLDebug.LogWarning("读取数据包长度不合法（不大于0或者超出缓冲区长度）, 自动断开连接");
                    break;
                case eNetError.NE_WriteBufferOverflow:
                    PLDebug.LogWarning("写入数据包长度不合法（不大于0或者超出缓冲区长度）, 自动断开连接");
                    break;
                case eNetError.NE_OutOfWorking:
                    PLDebug.LogWarning("网络无法工作, 自动断开连接");
                    mCurNetState = eNetState.NS_AgaionConnect;
                    break;
                case eNetError.NE_WriteException:
                    PLDebug.LogWarning("网络写入异常, 自动断开连接");
                    break;
            }

            mCurNetState = eNetState.NS_ConnectDropped;

            //this.CloseConnect();
        }

        public void Update()
        {
            switch (mCurNetState)
            {
                case eNetState.NS_None:

                    break;
                case eNetState.NS_Connecting:

                    break;
                case eNetState.NS_ConnectSuccessed:
                    mCurNetState = eNetState.NS_Working;
                    if (OnConnected != null)
                    {
                        OnConnected(this, null);
                    }

                    break;
                case eNetState.NS_ConnectFailed:

                    CloseConnect();

                    if (OnConnectFailed != null)
                    {
                        OnConnectFailed(this, null);
                    }

                    break;
                case eNetState.NS_Working:

                    // do nothing

                    break;
                case eNetState.NS_ConnectDropped:

                    CloseConnect();

                    if (OnDisconnected != null)
                    {
                        OnDisconnected(this, null);
                    }

                    break;
                case eNetState.NS_AgaionConnect:

                    mTcpClient.Close();
                    NewTcpClient();

                    break;
                default:
                    NetTool.LogError("unreachable..");
                    break;
            }
        }

        public bool IsStopped()
        {
            return NetState == eNetState.NS_None;
        }

        public bool IsWorking()
        {
            return NetState == eNetState.NS_Working;
        }

        public void StartConnect()
        {
            if (mCurNetState != eNetState.NS_None)
            {
                NetTool.LogWarning("Can not connect, because the netstate is" + mCurNetState.ToString());
                return;
            }

            mCurNetState = eNetState.NS_Connecting;
            NetTool.Log("Connect to " + mServerIpAdd + ":" + mServerPort);

            NewTcpClient();
        }

        private void NewTcpClient()
        {
            mTcpClient = new TcpClient
            {
                ReceiveBufferSize = NetDefine.BUFFER_LENGTH,
                SendBufferSize = NetDefine.BUFFER_LENGTH
            };
            mTcpClient.BeginConnect(mServerIpAdd, mServerPort, new AsyncCallback(ConnectCallback), null);
        }

        public void CloseConnect()
        {
            if (mCurNetState == eNetState.NS_None)
            {
                NetTool.LogWarning("There's no connection to close!");
                return;
            }

            mCurNetState = eNetState.NS_None;
            NetTool.Log("Close connection from " + mServerIpAdd + ":" + mServerPort);

            mNetStream = null;

            mTcpClient.Close();
            mTcpClient = null;
        }

        private void ConnectCallback(IAsyncResult result)
        {
            if (!result.IsCompleted)
            {
                return;
            }

            mTcpClient.EndConnect(result);
            if (mTcpClient.Connected)
            {
                mNetStream = mTcpClient.GetStream();
                ReceiveHeadLen();
                mCurNetState = eNetState.NS_ConnectSuccessed;
                NetTool.Log("Connecting successed.");
            }
            else
            {
                mCurNetState = eNetState.NS_ConnectFailed;
                NetTool.Log("Connecting failed.");
            }
        }

        private void ReceiveHeadLen()
        {
            mBufferInfo.nBufferSize = NetDefine.HEAD_LEN_SIZE;
            mBufferInfo.nReadSize = 0;
            mNetStream.BeginRead(mByHead, 0, NetDefine.HEAD_LEN_SIZE, HeadLenCallBack, null);
        }

        private void HeadLenCallBack(IAsyncResult result)
        {
            mBufferInfo.nReadSize += mNetStream.EndRead(result);

            // 如果包头没有读够数据那么就继续读取
            if (mBufferInfo.nReadSize < mBufferInfo.nBufferSize)
            {
                mNetStream.BeginRead(mByHead, mBufferInfo.nReadSize,
                    mBufferInfo.nBufferSize - mBufferInfo.nReadSize,HeadLenCallBack, null);        //继续异步读取
            }
            else
            {
                if (!BitConverter.IsLittleEndian)
                {
                    Array.Reverse(mByHead);
                }

                int nBufferSize = (int) BitConverter.ToUInt32(mByHead, 0) + NetDefine.HEAD_OP_SIZE;
                mBufferInfo.nBufferSize = nBufferSize;
                mBufferInfo.nReadSize = 0;
                if (nBufferSize > 0 && nBufferSize < NetDefine.BUFFER_LENGTH)
                {
                    mNetStream.BeginRead(mByReceiveBuf, 0, nBufferSize, ReceiveCallBack, null);
                }
                else
                {
                    NetDrop(eNetError.NE_ReadBufferOverflow);
                }
            }
        }

        private void ReceiveCallBack(IAsyncResult result)
        {
            mBufferInfo.nReadSize += mNetStream.EndRead(result);

            // 如果没有读够数据那么就继续读取
            if (mBufferInfo.nReadSize < mBufferInfo.nBufferSize)
            {
                
                mNetStream.BeginRead(mByReceiveBuf, mBufferInfo.nReadSize,
                    mBufferInfo.nBufferSize - mBufferInfo.nReadSize, new AsyncCallback(ReceiveCallBack), null);
            }
            else
            {
                if (OnReceived != null)
                {
                    OnReceived(this, new NetEventArg_Received(mByReceiveBuf, mBufferInfo.nReadSize));
                }

                this.ReceiveHeadLen();
            }
        }

        public void SendToServer(byte[] byData)
        {
            if (mCurNetState != eNetState.NS_Working)
            {
                NetDrop(eNetError.NE_OutOfWorking);
                return;
            }

            try
            {
                mNetStream.BeginWrite(byData, 0, byData.Length, null, null);
            }
            catch (Exception e)
            {
                PLDebug.LogError(e.ToString());
                NetDrop(eNetError.NE_WriteException);
            }

            //int length = byData.Length;
            //if (length > 0 && length < NetDefine.BUFFER_LENGTH)
            //{
            //    byte[] _data = new byte[length + NetDefine.HEAD_LENGTH];
            //    Array.Copy(BitConverter.GetBytes(length - NetDefine.OPCODE_LENGTH), 0, _data, 0, NetDefine.HEAD_LENGTH);
            //    Array.Copy(byData, 0, _data, NetDefine.HEAD_LENGTH, byData.Length);
            //    try
            //    {
            //        m_NetStream.BeginWrite(_data, 0, length + NetDefine.HEAD_LENGTH, null, null);
            //        return;
            //    }
            //    catch// (Exception e)
            //    {
            //        //NetTool.LogWarning("Write Error:" + e.ToString());
            //        _NetDrop(eNetError.NE_WriteException);
            //    }
            //}
            //else
            //{
            //    _NetDrop(eNetError.NE_WriteBufferOverflow);
            //}
        }
    }
} // end namespace